/*
 * @(#)DateUtils.java 1.0 2007-7-18
 * Copyright (c) 2007 BOCSoft Information Industrial (Shenzhen) Co., Ltd.
 * All rights reserved.
 */

package com.rfsp.common.util;

import java.sql.Timestamp;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * DateUtils.java
 * 日期时间工具
 * 
 * @version 1.0
 * @author SUNY
 * Written Date: 2007-7-18
 * 
 * Modified By: 
 * Modified Date:
 */
public class DateUtil {

	/** 缺省日期格式 */
	public static final String DEFAULT_DATE_FMT = "yyyy-MM-dd";

	/** 缺省时间格式 */
	public static final String DEFAULT_TIME_FMT = "yyyy/MM/dd HH:mm:ss";
	
	/** 缺省时间格式 yyyy-MM-dd HH:mm:ss */
	public static final String DEFAULT_TIME_FMT2 = "yyyy-MM-dd HH:mm:ss";
	
	/** 缺省时间格式yyyy-MM-dd */
	public static final String DEFAULT_TIME_FMT3 = "yyyyMMdd";

	public static final String DEFAULT_TIME_FMT5 = "yyyy年MM月dd日 HH时mm分ss秒";

	/** 全部时区名字 */
	private static final List TimeZoneIds = Arrays.asList(TimeZone.getAvailableIDs());

	/** 自定义时区缓存 */
	private static final Map TimeZoneCache = new HashMap();
	
	/**北京时区*/
	public static final TimeZone timeZoneBeijing = TimeZone.getTimeZone("Asia/Shanghai");

	public static final DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");


	/**
	 * 转换日期毫秒数为缺省日期格式字符串
	 *
	 * @param date
	 * @return
	 */
	public static String Date2String(long date) {
		return Date2String(new Date(date), DEFAULT_DATE_FMT, null);
	}

	/**
	 * 转换日期毫秒数为缺省日期格式字符串
	 *
	 * @param date
	 * @param timeZone
	 * @return
	 */
	public static String Date2String(long date, TimeZone timeZone) {
		return Date2String(new Date(date), DEFAULT_DATE_FMT, timeZone);
	}

	/**
	 * 转换日期为缺省日期格式字符串
	 *
	 * @param date
	 * @return
	 */
	public static String Date2String(Date date) {
		return Date2String(date, DEFAULT_DATE_FMT, null);
	}

	/**
	 * 转换日期为缺省日期格式字符串
	 *
	 * @param date
	 * @param timeZone
	 * @return
	 */
	public static String Date2String(Date date, TimeZone timeZone) {
		return Date2String(date, DEFAULT_DATE_FMT, timeZone);
	}

	/**
	 * 转换日期毫秒数为缺省日期格式字符串
	 *
	 * @param date
	 * @return
	 */
	public static String Time2String(long date) {
		return Date2String(new Date(date), DEFAULT_TIME_FMT2, null);
	}

	/**
	 * 转换日期毫秒数为缺省日期格式字符串
	 *
	 * @param date
	 * @param timeZone
	 * @return
	 */
	public static String Time2String(long date, TimeZone timeZone) {
		return Date2String(new Date(date), DEFAULT_TIME_FMT, timeZone);
	}

	/**
	 * 转换日期为缺省日期格式字符串
	 *
	 * @param date
	 * @return
	 */
	public static String Time2String(Date date) {
		return Date2String(date, DEFAULT_TIME_FMT2, null);
	}

	/**
	 * 转换日期为缺省日期格式字符串(精确到日)
	 *
	 * @param date
	 * @return
	 */
	public static String Time2String2(Date date) {
		return Date2String(date, DEFAULT_TIME_FMT3, null);
	}
	
	/**
	 * 转换日期为缺省日期格式字符串
	 *
	 * @param date
	 * @param timeZone
	 * @return
	 */
	public static String Time2String(Date date, TimeZone timeZone) {
		return Date2String(date, DEFAULT_TIME_FMT, timeZone);
	}

	/**
	 * 转换日期为指定格式字符串
	 *
	 * @param date
	 * @param format
	 * @return
	 */
	public static String Date2String(Date date, String format) {
		return Date2String(date, format, null);

	}

	/**
	 * 转换日期为指定格式字符串
	 *
	 * @param date
	 * @param format
	 * @param timeZone
	 * @return
	 */
	public static String Date2String(Date date, String format, TimeZone timeZone) {
		if (date == null || format == null)
			return "";
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		if (timeZone != null)
			sdf.setTimeZone(timeZone);
		return sdf.format(date);

	}

	/**
	 * 解析日期时间字符串,支持 yyMMdd,yyyyMMdd, yyyy-MM-dd, yyyy/MM/dd, yyyyMMddHHmm,
	 * yyyyMMddHHmmss, yyyyMMddHHmmssSSS, yyyy-MM-dd HH:mm:ss, yyyy-MM-dd HH:mm:ss.SSS 格式,
	 * 其它方式结果不保证正确
	 *
	 * @param str
	 * @return date
	 */
	public static Date String2Date(String str) {
		return String2Date(str, (TimeZone) null);
	}

	/**
	 * 解析日期时间字符串,支持 yyMMdd,yyyyMMdd, yyyy-MM-dd, yyyy/MM/dd, yyyyMMddHHmm,
	 * yyyyMMddHHmmss, yyyyMMddHHmmssSSS, yyyy-MM-dd HH:mm:ss, yyyy-MM-dd HH:mm:ss.SSS 格式,
	 * 其它方式结果不保证正确
	 *
	 * @param str
	 * @param timeZone
	 * @return date
	 */
	public static Date String2Date(String str, TimeZone timeZone) {
		if (str == null)
			return null;
		str = str.trim();
		if (Pattern.matches("^\\d{6}$", str))//(str.length() == 6)
			return String2Date(str, "yyMMdd", timeZone);
		if (Pattern.matches("^\\d{8}$", str))//(str.length() == 8)
			return String2Date(str, "yyyyMMdd", timeZone);
		//if (str.length() == 10) {
		if (Pattern.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$", str))//(str.indexOf("-") != -1)
			return String2Date(str, "yyyy-M-d", timeZone);

		if (Pattern.matches("^\\d{4}/\\d{1,2}/\\d{1,2}$", str))//(str.indexOf("/") != -1)
			return String2Date(str, "yyyy/M/d", timeZone);
		//}
		
		if (Pattern.matches("^\\d{12}$", str))//(str.length() == 12)
			return String2Date(str, "yyyyMMddHHmm", timeZone);
		if (Pattern.matches("^\\d{14}$", str))//(str.length() == 14)
			return String2Date(str, "yyyyMMddHHmmss", timeZone);
		if (Pattern.matches("^\\d{17}$", str))//(str.length() == 17)
			return String2Date(str, "yyyyMMddHHmmssSSS", timeZone);
//		if (str.length() == 19) {
		if (Pattern.matches("^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$", str))//(str.indexOf("-") != -1)
			return String2Date(str, "yyyy-M-d H:m:s", timeZone);
		if (Pattern.matches("^\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}$", str))//(str.indexOf("/") != -1)
			return String2Date(str, "yyyy/M/d H:m:s", timeZone);
//		}
		
//		if (str.length() == 23) {
		if (Pattern.matches("^\\d{4}-\\d{1,2}-\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{1,3}$", str))//(str.indexOf("-") != -1)
			return String2Date(str, "yyyy-M-d H:m:s.SSS", timeZone);
		if (Pattern.matches("^\\d{4}/\\d{1,2}/\\d{1,2} \\d{1,2}:\\d{1,2}:\\d{1,2}.\\d{1,3}$", str))//(str.indexOf("/") != -1)
			return String2Date(str, "yyyy/M/d H:m:s.SSS", timeZone);
//		}
		
		try {
			return new SimpleDateFormat().parse(str);
		} catch (ParseException e) {
			throw new RuntimeException("validation.date.parse_error");
		}
	}
	
	/**
	 * 按指定方式解析日期时间
	 *
	 * @param str
	 * @param format
	 * @return
	 */
	public static Date String2Date(String str, String format) {
		return String2Date(str, format, null);
	}

	/**
	 * 按指定方式解析日期时间
	 *
	 * @param str
	 * @param format
	 * @param timeZone
	 * @return
	 */
	public static Date String2Date(String str, String format, TimeZone timeZone) {
		if (str == null)
			return null;
		if (format == null)
			//format = DEFAULT_DATE_FMT;
			format = DEFAULT_TIME_FMT2;
		SimpleDateFormat fmt = new SimpleDateFormat(format);
		if (timeZone != null)
			fmt.setTimeZone(timeZone);
		try {
			return fmt.parse(str);
		} catch (ParseException e) {
			throw new RuntimeException("validation.date.parse_error", e);
		}
	}

	/**
	 * 比较两个日期是否是一天(不考虑时间)
	 *
	 * @param date1
	 * @param date2
	 * @return
	 */
	public static boolean isDateEqual(Date date1, Date date2) {
		return truncDate(date1).equals(truncDate(date2));
	}

	/**
	 * 比较两个日期是否是一天(不考虑时间)
	 *
	 * @param date1
	 * @param date2
	 * @param timeZone
	 * @return
	 */
	public static boolean isDateEqual(Date date1, Date date2, TimeZone timeZone) {
		return truncDate(date1, timeZone).equals(truncDate(date2, timeZone));
	}

	/**
	 * 返回某日零时整
	 *
	 * @param date
	 * @return
	 */
	public static Date truncDate(Date date) {
		return truncDate(date, Calendar.DATE);
	}

	/**
	 * 返回某日零时整
	 *
	 * @param date
	 * @param timeZone
	 * @return
	 */
	public static Date truncDate(Date date, TimeZone timeZone) {
		return truncDate(date, Calendar.DATE, timeZone);
	}

	/**
	 * 日期时间取整，支持年、月、周、日、时、分、秒
	 *
	 * @param date
	 * @param mode
	 * @return date
	 */
	public static Date truncDate(Date date, int mode) {
		return truncDate(date, mode, null);
	}

	/**
	 * 日期时间取整，支持年、月、周、日、时、分、秒
	 *
	 * @param date
	 * @param mode
	 * @param timeZone
	 * @return date
	 */
	public static Date truncDate(Date date, int mode, TimeZone timeZone) {
		if (date == null)
			return null;
		Calendar cal = (timeZone == null ? Calendar.getInstance() : Calendar.getInstance(timeZone));
		cal.setTime(date);
		switch (mode) {
		case Calendar.YEAR:
			cal.clear(Calendar.MONTH);
		case Calendar.MONTH:
		case Calendar.WEEK_OF_MONTH:
			if (mode == Calendar.MONTH)
				cal.set(Calendar.DAY_OF_MONTH, 1);
			else
				cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
		case Calendar.DATE:
			cal.set(Calendar.HOUR_OF_DAY, 0);
		case Calendar.HOUR:
			cal.clear(Calendar.MINUTE);
		case Calendar.MINUTE:
			cal.clear(Calendar.SECOND);
		case Calendar.SECOND:
			cal.clear(Calendar.MILLISECOND);
			break;
		default:
			throw new IllegalArgumentException();
		}
		return cal.getTime();
	}

	/**
	 * 循环调整时间
	 *
	 * @param date
	 * @param field
	 * @param amount
	 * @return
	 */
	public static Date rollDate(Date date, int field, int amount) {
		return rollDate(date, field, amount, null);
	}

	/**
	 * 循环调整时间
	 *
	 * @param date
	 * @param field
	 * @param amount
	 * @param timeZone
	 * @return
	 */
	public static Date rollDate(Date date, int field, int amount, TimeZone timeZone) {
		Calendar cal = (timeZone == null ? Calendar.getInstance() : Calendar.getInstance(timeZone));
		cal.setTime(date);
		cal.roll(field, amount);
		return cal.getTime();
	}

	/**
	 * 调整时间
	 *
	 * @param date
	 * @param field
	 * @param amount
	 * @return
	 */
	public static Date addDate(Date date, int field, int amount) {
		return addDate(date, field, amount, null);
	}

	/**
	 * 调整时间
	 *
	 * @param date
	 * @param field
	 * @param amount
	 * @param timeZone
	 * @return
	 */
	public static Date addDate(Date date, int field, int amount, TimeZone timeZone) {
		Calendar cal = (timeZone == null ? Calendar.getInstance() : Calendar.getInstance(timeZone));
		cal.setTime(date);
		cal.add(field, amount);
		return cal.getTime();
	}

	/**
	 * 比较2个同时区时间先后，注意:时间的格式必须在String2Date支持的格式范围内
	 *
	 * @param date1 时间1
	 * @param date2 时间2
	 * @return 如果时间1等于时间2，返回0，如果时间1小于时间2，返回负值，如果时间1大于时间2，返回正值
	 */
	public static int compare(String date1, String date2) {
		return String2Date(date1).compareTo(String2Date(date2));
	}

	/**
	 * 比较2个时间先后
	 *
	 * @param date1 时间1
	 * @param date2 时间2
	 * @return 如果时间1等于时间2，返回0，如果时间1小于时间2，返回负值，如果时间1大于时间2，返回正值
	 */
	public static int compare(Date date1, Date date2) {
		return date1.compareTo(date2);
	}
	
	/**
	 * 按格式比较2个时间先后
	 * @param date1 时间1
	 * @param date2 时间2
	 * @param format 格式
	 * @return 如果时间1等于时间2，返回0，如果时间1小于时间2，返回负值，如果时间1大于时间2，返回正值
	 * @author liuzhy
	 * @date 2011-3-3
	 */
	public static int compare(Date date1, Date date2, String format){
		return DateUtil.String2Date(DateUtil.Date2String(date1, format), format)
			.compareTo(DateUtil.String2Date(DateUtil.Date2String( date2, format), format));
	}

	/**
	 * 查询时区
	 *
	 * @param timediff
	 * @return
	 */
	public static TimeZone findTimeZone(int timediff) {
		String[] ids = TimeZone.getAvailableIDs(timediff * 60000 + TimeZone.getDefault().getOffset(System.currentTimeMillis()));
		if (ids == null)
			return new SimpleTimeZone(timediff * 60000, "UDT");
		return TimeZone.getTimeZone(ids[0]);
	}

	/**
	 * 根据时区名字取得时区
	 * 如果非java已知标准名字，则必须为 GMT[+-]hh:mm 格式
	 * @param id
	 */
	public static TimeZone getTimeZone(String id) {
		if (id == null)
			return null;
		if (TimeZoneIds.contains(id))
			return TimeZone.getTimeZone(id);
		if (TimeZoneCache.containsKey(id))
			return (TimeZone) TimeZoneCache.get(id);
		Pattern p = Pattern.compile("^GMT[+-](0[0-9]|1[01]):([0-5][0-9])$");
		Matcher m = p.matcher("id");
		if (!m.matches())
			return null;
		int hh = Integer.parseInt(id.substring(4, 6));
		int mm = Integer.parseInt(id.substring(7));
		int sign = (id.charAt(3) == '-' ? -1 : 1);
		TimeZone tz = new SimpleTimeZone((hh * 60 + mm) * 60000 * sign, id);
		TimeZoneCache.put(id, tz);
		return tz;
	}

	public static Timestamp string2TimeStamp(Object millions, Object nanos) {
		try {
			Timestamp res = new Timestamp(Long.parseLong((String) millions));
			res.setNanos(Integer.parseInt((String) nanos));

			return res;
		} catch (Exception e) {
			return null;
		}

	}

	/** 把Date转为Timestamp */
	public static Timestamp date2Timestamp(Date adate) {
		return new Timestamp(adate.getTime());
	}

	/**
	 * <p>
	 * 把用户当地时间转成系统时间。
	 * </p>
	 *
	 * @param date 待转换的时间。
	 * @param dest 用户所在时区。
	 * @return 转换后的时间。
	 */
	public static Date transformDateFrom(Date date, TimeZone dest) {

		long offset = dest.getOffset(date.getTime()) - timeZoneBeijing.getOffset(date.getTime());
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(date.getTime() - offset);
		return cal.getTime();
	}

	/**
	 * <p>
	 * 把系统时间转成用户当地时间。
	 * </p>
	 *
	 * @param date 待转换的时间。
	 * @param dest 用户所在时区。
	 * @return 转换后的时间。
	 */
	public static Date transformDateInto(Date date, TimeZone dest) {
		long offset = dest.getOffset(date.getTime()) - timeZoneBeijing.getOffset(date.getTime());
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(date.getTime() + offset);
		return cal.getTime();
	}

	/**
	 * 校验起始日期和结束日期的合法性<p>
	 * 例如：起始日期距当前日期不超过12个月，起始结束日期间隔不超过3个月，调用<br>
	 * validateDateRange(startDate, endDate, currentDate, 3, 12)
	 * 
	 * @param startDate 起始日期
	 * @param endDate   结束日期
	 * @param currentDate 当前日期
	 * @param maxInterval 起始日期和结束日期的最大距离（单位为月）
	 * @param amount      起始日期和当前日期的最大距离（单位为月）
	 * @return
	 */
	public static boolean validateDateRange(Date startDate, Date endDate, Date currentDate, int maxInterval, int amount) {
		if (startDate.after(endDate))
			return false;

		if (currentDate.after(addDate(startDate, Calendar.MONTH, amount)))
			return false;

		if (endDate.after(addDate(startDate, Calendar.MONTH, maxInterval)))
			return false;

		return true;
	}
	
	//add by lyp
	public static boolean validateDateRangeday(Date startDate, Date currentDate, int amount) {

		if (currentDate.after(addDate(startDate, Calendar.DATE, amount)))
			return false;


		return true;
	}
	
	/**
	 * 校验日期字符串是否是有效的格式
	 * @param dateStr 日期字符串
	 * @param format 日期格式
	 * @return
	 * @author liuzhy
	 * @date 2011-1-22
	 */
	public static boolean checkDateValid(String dateStr, String format){
		Date d;
		try {
			d = DateUtil.String2Date(dateStr, format);
		} catch (Exception e) {
			return false;
		}
		if(DateUtil.Date2String(d, format).equals(dateStr)){
			return true;
		}else {
			return false;
		}
	}
	
    /**
     * 日期运算
     *@author lvxk
     *@date   2011-1-28
     * @param endDate
     * @param day
     * @return
     */
    public static Date dateAddDay(Date endDate,int count){
    	if(endDate == null){
    		return null;
    	}
    	Calendar cal=Calendar.getInstance();
		cal.setTime(endDate);
		cal.add(cal.DAY_OF_MONTH, count);
		endDate = cal.getTime();
		
		return endDate;
    }

    /**
     * 是否是最后一天
     *@author dp
     *@date   2014-8-18
     * @param endDate
     * @param day
     * @return
     */
    public static boolean isLastDayOfMonth(String end){
//    	Date endDate=DateUtil.String2Date(end,"ddmmyyyy");
    	Date endDate=new Date();
    	int dd=Integer.parseInt(end.substring(0,2));
    	int mm=Integer.parseInt(end.substring(2,4));
    	int yyyy=Integer.parseInt(end.substring(4,8));
    	if (mm==1||mm==3||mm==5||mm==7||mm==8||mm==10||mm==12) {
			if(dd!=31)
				return false;
			else {
				return true;
			}
		}
    	if (mm==4||mm==6||mm==9||mm==11) {
			if(dd!=30)
				return false;
			else {
				return true;
			}
		}
    	if(yyyy/4==0&&dd==29)
    		return true;
    	else if (yyyy/4!=0&&dd==28) {
			return true;
		}else {
			return false;
		}
 
    }
    
   
    /**
	 * 将日期转换为指定格式
	 * 
	 * @param date
	 * @param format
	 *            如 yyyy-MM-dd HH:mm:ss等
	 * @return
	 */
	public static Date covertDateFormat(Date date, String format) {
		if (date == null) {
			return null;
		} else {
			SimpleDateFormat sdf = new SimpleDateFormat(format);
			try {
				date = sdf.parse(sdf.format(date));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
		return date;
	}
	/**
	 * 根据差距的天数获取日期对象
	 * 
	 * @param date
	 *            Date 指定的日期对象
	 * @param day
	 *            int 差距时间 <br>
	 *            day > 0 表示获取指定日期day天之后的日期 <br>
	 *            day < 0 表示获取指定日期day天之前的日期 <br>
	 * @return Date
	 */
	public static Date getDifferenceDateByDay(Date date, int day) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		calendar.add(GregorianCalendar.DATE, day);
		return calendar.getTime();
	}
	
	
	
	
	public static String dateMinus6Month(String date ) throws Exception {
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
        Date dt;
		dt = sdf.parse(date);
        Calendar rightNow = Calendar.getInstance();
        rightNow.setTime(dt);
        rightNow.add(Calendar.MONTH,-6);//日期加3个月
        Date dt1=rightNow.getTime();
        String reStr = sdf.format(dt1);
        return reStr;
	}

	public static String Date7String(Date date) {
		return Date2String(date, DEFAULT_TIME_FMT5, null);
	}



	public static String parseDateToStrByYYYYMMDD(Date date) {
		if (date == null) {
			return "";
		}
		return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().format(YYYY_MM_DD);
	}

	public static String parseStrByYYYYMMDD(LocalDate date) {
		return date.format(YYYY_MM_DD);
	}

	public static Date parseDateByYYYYMMDD(String date) {
		LocalDate localDate = LocalDate.parse(date, YYYY_MM_DD);
		return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
	}

	public static LocalDate parseLocalDateByYYYYMMDD(String date) {
		return LocalDate.parse(date, YYYY_MM_DD);
	}


	/**
	 * 获取当前时间的 yyyyMMdd 或 HHmmss
	 * @param simpleDateFormat
	 * @return
	 * @throws Exception
	 */
	public static String dateFormatStr(String simpleDateFormat) throws Exception {
		String formattedDate = "";
		if("yyyyMMdd".equals(simpleDateFormat)){
			Date currentDate = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
			formattedDate = sdf.format(currentDate);
		}else{
			// 获取当前时间
			LocalTime currentTime = LocalTime.now();
			// 定义时间格式化器
			DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmmss");
			// 格式化当前时间为HHMMSS格式
			formattedDate = currentTime.format(formatter);
		}

		return formattedDate;
	}

		public static boolean isValidDate(String dateStr, String format) {
			SimpleDateFormat sdf = new SimpleDateFormat(format);
			sdf.setLenient(false); // 设置严格解析
			try {
				Date date = sdf.parse(dateStr);
				return dateStr.equals(sdf.format(date)); // 再次格式化并比较，确保格式完全正确
			} catch (ParseException e) {
				return false;
			}
		}
	
}